#include "Data/CurveData.hpp"

#include <algorithm>
#include <stdint.h>

CurveData::CurveData()
{
    ClassName="CurveData";
}

CurveData::CurveData(std::vector< Point2DData* > &points)
{
    ClassName="CurveData"; 
    m_points.resize( points.size() );
    std::copy( points.begin(), points.end(), m_points.begin() );

#if defined(_MSC_VER) && _MSC_VER <= 1600
	for (auto point = m_points.begin(); point != m_points.end(); ++point)
        (*point)->addObserver(this);
#else
    for (auto point : m_points)
        point->addObserver(this);
#endif
}

int CurveData::load(std::istream &in)
{
    uint64_t n;
    in >> n;
    double x, y;
    for(uint64_t i = 0; i < n; ++i)
    {
        in >> x >> y;
        m_points.push_back(new Point2DData(x, y));
    }
    updateNotification();        
    return 0;
}

int CurveData::save(std::ostream& out) const
{
    out << m_points.size() << std::endl;

#if defined(_MSC_VER) && _MSC_VER <= 1600
	for (auto point = m_points.begin(); point != m_points.end(); ++point)
        out << (*point)->x() << "\t" << (*point)->y() << std::endl;
#else
    for (auto point : m_points)
        out << point->x() << "\t" << point->y() << std::endl;
#endif

    return 0;
}

void CurveData::addPoint(Point2DData* p)
{
    p->addObserver(this);
    m_points.push_back(p);
    updateNotification();
}

void CurveData::removePoint(Point2DData* p)
{
    m_points.erase( 
        remove( m_points.begin(), m_points.end(), p ),
        m_points.end()
    );
    p->removeObserver(this);
    updateNotification();
}

void CurveData::update()
{
    updateNotification();
}

const std::vector< Point2DData* >& CurveData::getPoints() const
{
    return m_points;
}

CurveData::~CurveData()
{
    //for( auto point : m_points )
      //  delete point;
}

CurveData CurveData::operator + (const CurveData &p) const 
{
    CurveData v2;	
    for(std::vector<Point2DData*>::const_iterator pointi = m_points.begin(); pointi != m_points.end(); pointi++)
    {
        double x0, y0, x1, y1, x2, y2;
        x0 = (*pointi)->x();
        y0 = (*pointi)->y(); ///получаем точку в выделенной кривой
        bool flag = true;
        for(std::vector<Point2DData*>::const_iterator pointj = p.m_points.begin(); pointj != p.m_points.end()-1 && flag; pointj++)//перебираем все пары точек второй кривых
        {
            x1 = (*pointj)->x();
            y1 = (*pointj)->y();
            if(x0 == x1) ///если совпадают, то просто складываем 'y'
            {	
                y1 = y1 + y0;
                v2.m_points.push_back(new Point2DData(x1, y1));
                flag = false;
            }
            if(x0 > x1)
            {
                x2 = (*(pointj+1))->x();
                y2 = (*(pointj+1))->y();
                if(x0 == x2)
                {
                    y2 = y2 + y0; ///если совпадают, то просто складываем 'y'
                    v2.m_points.push_back(new Point2DData(x2, y2));
                    flag = false;
                }
                if(x0 < x2) ///считаем интерполяцию
                {
                    double k, b;
                    k = (y2 - y1) / (x2 - x1);
                    b = y1 - k * x1;
                    y1 = k * x0 + b;
                    y1 = y1 + y0;
                    v2.m_points.push_back(new Point2DData(x0, y1));
                    flag = false;
                }
            }
        }
    }
	  return v2;
}

CurveData CurveData::operator - (const CurveData &p) const
{
	  CurveData v2;	
	  for(std::vector<Point2DData*>::const_iterator pointi = m_points.begin(); pointi != m_points.end(); pointi++)
    {
        double x0, y0, x1, y1, x2, y2;
        x0 = (*pointi)->x();
        y0 = (*pointi)->y(); ///получаем точку в выделенной кривой
        bool flag = true;
        for(std::vector<Point2DData*>::const_iterator pointj = p.m_points.begin(); pointj != p.m_points.end()-1 && flag; pointj++)//перебираем все пары точек второй кривых
        {
            x1 = (*pointj)->x();
            y1 = (*pointj)->y();
            if(x0 == x1) ///если совпадают, то просто вычитаем'y'
            {	
                y1 = y0 - y1;
                v2.m_points.push_back(new Point2DData(x1, y1));
                flag = false;
            }
            if(x0 > x1)
            {
                x2 = (*(pointj+1))->x();
                y2 = (*(pointj+1))->y();
                if(x0 == x2)
                {
                    y2 = y0 - y2; ///если совпадают, то просто вычитаем 'y'
                    v2.m_points.push_back(new Point2DData(x2, y2));
                    flag = false;
                }
                if(x0 < x2) ///считаем интерполяцию
                {
                    double k, b;
                    k = (y2 - y1) / (x2 - x1);
                    b = y1 - k * x1;
                    y1 = k * x0 + b;
                    y1 = y0 - y1;
                    v2.m_points.push_back(new Point2DData(x0, y1));
                    flag = false;
                }
            }
        }
    }
	  return v2;
}

CurveData CurveData::operator * (const CurveData &p) const
{
	  CurveData v2;	
	  for(std::vector<Point2DData*>::const_iterator pointi = m_points.begin(); pointi != m_points.end(); pointi++)
    {
        double x0, y0, x1, y1, x2, y2;
        x0 = (*pointi)->x();
        y0 = (*pointi)->y(); ///получаем точку в выделенной кривой
        bool flag = true;
        for(std::vector<Point2DData*>::const_iterator pointj = p.m_points.begin(); pointj != p.m_points.end()-1 && flag; pointj++)//перебираем все пары точек второй кривых
        {
            x1 = (*pointj)->x();
            y1 = (*pointj)->y();
            if(x0 == x1) ///если совпадают, то просто умножаем 'y'
            {	
                 y1 = y0 * y1;
                 v2.m_points.push_back(new Point2DData(x1, y1));
                 flag = false;
            }
            if(x0 > x1)
            {
                x2 = (*(pointj+1))->x();
                y2 = (*(pointj+1))->y();
                if(x0 == x2)
                {
                    y2 = y0 * y2; ///если совпадают, то просто умножаем 'y'
                    v2.m_points.push_back(new Point2DData(x2, y2));
                    flag = false;
                }
                if(x0 < x2) ///считаем интерполяцию
                {
                    double k, b;
                    k = (y2 - y1) / (x2 - x1);
                    b = y1 - k * x1;
                    y1 = k * x0 + b;
                    y1 = y0 * y1;
                    v2.m_points.push_back(new Point2DData(x0, y1));
                    flag = false;
                }
            }
        }
    }
	  return v2;
}

CurveData CurveData::operator / (const CurveData &p) const
{
	  CurveData v2;	
	  for(std::vector<Point2DData*>::const_iterator pointi = m_points.begin(); pointi != m_points.end(); pointi++)
    {
        double x0, y0, x1, y1, x2, y2;
        x0 = (*pointi)->x();
        y0 = (*pointi)->y(); ///получаем точку в выделенной кривой
        bool flag = true;
        for(std::vector<Point2DData*>::const_iterator pointj = p.m_points.begin(); pointj != p.m_points.end()-1 && flag; pointj++)//перебираем все пары точек второй кривых
        {
            x1 = (*pointj)->x();
            y1 = (*pointj)->y();
            if(x0 == x1) ///если совпадают, то просто делим 'y'
            {	
                y1 = y0 / y1;
                v2.m_points.push_back(new Point2DData(x1, y1));
                flag = false;
            }
            if(x0 > x1)
            {
                x2 = (*(pointj+1))->x();
                y2 = (*(pointj+1))->y();
                if(x0 == x2)
                {
                    y2 = y0 / y2; ///если совпадают, то просто делим 'y'
                    v2.m_points.push_back(new Point2DData(x2, y2));
                    flag = false;
                }
                if(x0 < x2) ///считаем интерполяцию
                {
                    double k, b;
                    k = (y2 - y1) / (x2 - x1);
                    b = y1 - k * x1;
                    y1 = k * x0 + b;
                    y1 = y0 / y1;
                    v2.m_points.push_back(new Point2DData(x0, y1));
                    flag = false;
                }
            }
        }
    }
    return v2;
}

CurveData CurveData::operator =(const CurveData &p)
{
	  m_points.resize( p.m_points.size() );
    std::copy( p.m_points.begin(), p.m_points.end(), m_points.begin() );
	  for(std::vector<Point2DData*>::iterator point = m_points.begin(); point != m_points.end(); point++)
        (*point)->addObserver(this);

	  updateNotification();
	  return *this;
}

CurveData CurveData::operator-() const
{
	CurveData v2;	
	for(std::vector<Point2DData*>::const_iterator point = m_points.begin(); point != m_points.end(); point++)
	{
		double x, y;
		x = (*point)->x();
		y = (*point)->y();
		v2.m_points.push_back(new Point2DData(x, y));
	}
	return v2;
}

const int CurveData::size() const
{
	return m_points.size();
}

void CurveData::sign()
{
	for(std::vector<Point2DData*>::iterator point = m_points.begin(); point != m_points.end(); point++)
		(*point)->setY(-(*point)->y());

	updateNotification();	
}

template<typename Arhive>
void CurveData::serialize(Arhive &ar, const unsigned int )
{
    ar & ClassName &   m_points ;
}

const double CurveData::curve(const double x) const
{
	 std::vector<Point2DData*>::const_iterator pointi = m_points.begin();
	 if((*pointi)->x() <= x)
   {
      for(; pointi != m_points.end(); pointi++)
      {
         if((*pointi)->x() > x)
         {
            double x1, y1, x2, y2;
            x1 = (*(pointi-1))->x();
            y1 = (*(pointi-1))->y();
            x2 = (*pointi)->x();
            y2 = (*pointi)->y();
            double k, b;
            k = (y2 - y1) / (x2 - x1);
            b = y1 - k * x1;
            y1 = k * x + b;
            return y1;
         }
      }
   }
	 return 0.0;
}

 CurveData CurveData::operator +(const double &c) const
 {
	  CurveData v2;	
	  for(std::vector<Point2DData*>::const_iterator pointi = m_points.begin(); pointi != m_points.end(); pointi++)
    {
        double x0, y0;
        x0 = (*pointi)->x();
        y0 = (*pointi)->y(); ///получаем точку в выделенной кривой
        v2.m_points.push_back(new Point2DData(x0, c+y0));
	  }
	  return v2;
 }

 CurveData CurveData::operator -(const double &c) const
 {
	  CurveData v2;	
	  for(std::vector<Point2DData*>::const_iterator pointi = m_points.begin(); pointi != m_points.end(); pointi++)
    {
        double x0, y0;
        x0 = (*pointi)->x();
        y0 = (*pointi)->y(); ///получаем точку в выделенной кривой
        v2.m_points.push_back(new Point2DData(x0, y0-c));
     }
     return v2;
 }

 CurveData CurveData::operator *(const double &c) const
 {
    CurveData v2;	
    for(std::vector<Point2DData*>::const_iterator pointi = m_points.begin(); pointi != m_points.end(); pointi++)
    {
        double x0, y0;
        x0 = (*pointi)->x();
        y0 = (*pointi)->y(); ///получаем точку в выделенной кривой
        v2.m_points.push_back(new Point2DData(x0, c*y0));
	  }
    return v2;
 }

 CurveData CurveData::operator /(const double &c) const
 {
	  CurveData v2;	
	  for(std::vector<Point2DData*>::const_iterator pointi = m_points.begin(); pointi != m_points.end(); pointi++)
    {
        double x0, y0;
        x0 = (*pointi)->x();
        y0 = (*pointi)->y(); ///получаем точку в выделенной кривой
        v2.m_points.push_back(new Point2DData(x0, y0/c));
	  }
	  return v2;
 }

